﻿using System;
using System.Collections.Generic;
using System.Linq;
using PES.Common.DAL;
using log4net;

namespace PES.Common.BLL
{
    using Model;

    public class PartidaBLL
    {
        private static ILog s_logger;
        private static ILog Logger
        {
            get { return s_logger ?? (s_logger = LogManager.GetLogger("PartidaBLL")); }
        }

        private static PartidaDAL s_partidaDAL;
        private static PartidaDAL PartDAL
        {
            get { return s_partidaDAL ?? (s_partidaDAL = new PartidaDAL()); }
        }

        private static CampeonatoDAL s_campDAL;
        private static CampeonatoDAL CampDAL
        {
            get { return s_campDAL ?? (s_campDAL = new CampeonatoDAL()); }
        }

        private static PontuacaoDAL s_pontDAL;
        private static PontuacaoDAL PontDAL
        {
            get { return s_pontDAL ?? (s_pontDAL = new PontuacaoDAL()); }
        }

        private static JogadorVirtualDAL s_jogVirtualDAL;
        private static JogadorVirtualDAL JogVirtualDAL
        {
            get { return s_jogVirtualDAL ?? (s_jogVirtualDAL = new JogadorVirtualDAL()); }
        }

        private static JogadorDAL s_jogDAL;
        private static JogadorDAL JogDAL
        {
            get { return s_jogDAL ?? (s_jogDAL = new JogadorDAL()); }
        }

        public static void CadastrarPartida(Campeonato _campeonato, Partida _partida)
        {
            try
            {
                if (_campeonato == null || _campeonato.ID <= 0)
                {
                    throw new ApplicationException("Campeonato inválido.");
                }
                if (_partida == null)
                {
                    throw new ApplicationException("Partida inválida.");
                }
                if (_partida.Jogador1 == null || _partida.Jogador1.ID <= 0)
                {
                    throw new ApplicationException("Jogador1 da partida não pode ser nulo.");
                }
                if (_partida.Jogador2 == null || _partida.Jogador2.ID <= 0)
                {
                    throw new ApplicationException("Jogador2 da partida não pode ser nulo.");
                }

                PartDAL.CadastrarPartida(_campeonato.ID, _partida);
                Campeonato camp = CampDAL.BuscarCampeonato(_campeonato.ID);
                
                if (camp.EstiloCampeonato != TipoCampeonato.PONTOS_CORRIDOS_DOIS_TURNOS &&
                    camp.EstiloCampeonato != TipoCampeonato.PONTOS_CORRIDOS_DOIS_TURNOS_COM_FINAIS) return;

                if (_partida.Rodada > (camp.QuantidadeRodadas/2)) return;

                int rodada = _partida.Rodada + (camp.QuantidadeRodadas / 2);
                var partSegundoTurno = new Partida
                                           {
                                               Jogador1 = _partida.Jogador2,
                                               Jogador2 = _partida.Jogador1,
                                               Rodada = rodada,
                                               Data = _partida.Data.AddDays(30)
                                           };
                PartDAL.CadastrarPartida(camp.ID, partSegundoTurno);
            }
            catch (Exception ex)
            {
                Logger.Error("Ocorreu um erro ao cadastrar a partida.", ex);
                throw;
            }
        }

        public static IList<Partida> BuscarPartidasPorRodada(Campeonato _campeonato, int _rodada)
        {
            try
            {
                if (_campeonato == null || _campeonato.ID <= 0)
                {
                    throw new ApplicationException("Campeonato inválido.");
                }

                return PartDAL.BuscarPartidasPorRodada(_campeonato.ID, _rodada);
            }
            catch (Exception ex)
            {
                Logger.Error("Ocorreu um erro ao buscar partidas.", ex);
                throw;
            }
        }

        public static IList<Partida> BuscarPartidasPorJogador(Campeonato _campeonato, Jogador _jogador)
        {
            try
            {
                if (_campeonato == null || _campeonato.ID <= 0)
                {
                    throw new ApplicationException("Campeonato inválido.");
                }

                if (_jogador == null || _jogador.ID <= 0)
                {
                    throw new ApplicationException("Jogador inválido.");
                }

                return PartDAL.BuscarPartidasPorJogador(_campeonato.ID, _jogador.ID);
            }
            catch (Exception ex)
            {
                Logger.Error("Ocorreu um erro ao buscar partidas.", ex);
                throw;
            }
        }

        public static void RegistraPlacar(Campeonato _campeonato, Partida _partida)
        {
            try
            {
                if (_campeonato == null || _campeonato.ID <= 0)
                {
                    throw new ApplicationException("Campeonato inválido.");
                }
                if (_partida == null || _partida.ID <= 0)
                {
                    throw new ApplicationException("Partida inválida.");
                }

                Partida partAntesAtualizar = PartDAL.BuscarPartida(_partida.ID);
                Campeonato camp = CampDAL.BuscarCampeonato(_campeonato.ID);

                //Verifica se o usuário está tendando modificar o placar ou 
                //registrando o placar pela primeira vez
                if (partAntesAtualizar.PlacarRegistrado)
                {
                    //Placar já estava registrado antes, verifica se o usuário está
                    //tentando modificando placar
                    if (partAntesAtualizar.GolsJogador1.Value != _partida.GolsJogador1.Value ||
                        partAntesAtualizar.GolsJogador2.Value != _partida.GolsJogador2.Value)
                    {
                        Pontuacao pontuacaoJog1 = PontDAL.BuscarPontuacaoJogador(_partida.Jogador1.ID);
                        Pontuacao pontuacaoJog2 = PontDAL.BuscarPontuacaoJogador(_partida.Jogador2.ID);

                        pontuacaoJog1.GolsPro -= partAntesAtualizar.GolsJogador1.Value;
                        pontuacaoJog1.GolsContra -= partAntesAtualizar.GolsJogador2.Value;
                        pontuacaoJog2.GolsPro -= partAntesAtualizar.GolsJogador2.Value;
                        pontuacaoJog2.GolsContra -= partAntesAtualizar.GolsJogador1.Value;

                        //Verificamos se houve mudança em quem ganhou, se não houve, apenas
                        //atualiza o placar sem se preocupar com a pontuação
                        bool jogador1GanhouAntes = false;
                        bool empateAntes = false;

                        if (partAntesAtualizar.GolsJogador1 > partAntesAtualizar.GolsJogador2)
                        {
                            jogador1GanhouAntes = true;
                        }
                        else if (partAntesAtualizar.GolsJogador1 == partAntesAtualizar.GolsJogador2)
                        {
                            empateAntes = true;
                        }

                        if (_partida.GolsJogador1 > _partida.GolsJogador2)
                        {
                            if (!jogador1GanhouAntes)
                            {
                                if (empateAntes)
                                {
                                    //Remove empate dos dois jogadores
                                    pontuacaoJog1.NumEmpates--;
                                    pontuacaoJog2.NumEmpates--;
                                }
                                else
                                {
                                    //Remove vitória do jogador 2
                                    pontuacaoJog2.NumVitorias--;
                                    //Remove a derrota do jogador 1
                                    pontuacaoJog1.NumDerrotas--;
                                }
                            }
                            else
                            {
                                pontuacaoJog1.NumVitorias--;
                                pontuacaoJog2.NumDerrotas--;
                            }
                        }
                        else if (_partida.GolsJogador1 == _partida.GolsJogador2)
                        {
                            if (!empateAntes)
                            {
                                if (jogador1GanhouAntes)
                                {
                                    //Remove vitória jogador 1
                                    pontuacaoJog1.NumVitorias--;
                                    //Remove derrota jogador 2
                                    pontuacaoJog2.NumDerrotas--;
                                }
                                else
                                {
                                    //Remove vitória jogador 2
                                    pontuacaoJog2.NumVitorias--;
                                    //Remove derrota jogador 1
                                    pontuacaoJog1.NumDerrotas--;
                                }
                            }
                            else
                            {
                                pontuacaoJog2.NumEmpates--;
                                pontuacaoJog1.NumEmpates--;
                            }
                        }
                        else
                        {
                            //Vitória do jogador 2
                            if (jogador1GanhouAntes)
                            {
                                //Remove vitória jogador 1
                                pontuacaoJog1.NumVitorias--;
                                //Remove derrota jogador 2
                                pontuacaoJog2.NumDerrotas--;
                            }
                            else if(empateAntes)
                            {
                                //Remove empate dois dois jogadores
                                pontuacaoJog1.NumEmpates--;
                                pontuacaoJog2.NumEmpates--;
                            }
                            else
                            {
                                //Remove vitória jogador 2
                                pontuacaoJog2.NumVitorias--;
                                //Remove derrota jogador 1
                                pontuacaoJog1.NumDerrotas--;
                            }
                        }

                        AtualizaPontuacao(pontuacaoJog1, pontuacaoJog2, _partida.GolsJogador1.Value,
                            _partida.GolsJogador2.Value);
                    }

                    PartDAL.AtualizaPartida(_partida);
                }//if placar já registrado
                else
                {
                    if (_partida.Rodada <= camp.QuantidadeRodadas)
                    {
                        _partida.PlacarRegistrado = _partida.GolsJogador1.HasValue;
                        PartDAL.AtualizaPartida(_partida);
                        if (_partida.PlacarRegistrado)
                        {
                            var pontuacaoJog1 = PontDAL.BuscarPontuacaoJogador(_partida.Jogador1.ID);
                            var pontuacaoJog2 = PontDAL.BuscarPontuacaoJogador(_partida.Jogador2.ID);

                            AtualizaPontuacao(pontuacaoJog1, pontuacaoJog2, _partida.GolsJogador1.Value,
                                _partida.GolsJogador2.Value);

                            LimpaSuspensoes(_partida.Jogador1);
                            LimpaSuspensoes(_partida.Jogador2);
                        }

                        if(PartDAL.AcabouPontosCorridos(_partida.Campeonato.ID))
                        {
                            //hora de cadastrar as partidas finais
                            var partidasDasFinais = BuscarJogosFinais(camp);
                            foreach (var partida in partidasDasFinais)
                            {
                                PartDAL.CadastrarPartida(camp.ID, partida);
                            }
                        }
                    }
                    else
                    {
                        //TODO: TERMINAR ISTO, AINDA NÃO ESTÁ PRONTO
                        //está atualizando partidas finais
                        _partida.PlacarRegistrado = true;
                        PartDAL.AtualizaPartida(_partida);
                        LimpaSuspensoes(_partida.Jogador1);
                        LimpaSuspensoes(_partida.Jogador2);

                        bool jogador1Ganhou = _partida.GolsJogador1.Value > _partida.GolsJogador2;
                        bool empate = _partida.GolsJogador1.Value == _partida.GolsJogador2;

                        switch (camp.QtdeTimesClassificadosFinais)
                        {
                            case TipoFinaisPontosCorridos.FINAL:
                                //Não precisa fazer nada
                                break;
                            case TipoFinaisPontosCorridos.SEMI:
                                //Calcula a final
                                Partida proximaPartida;
                                var proximasPartidas =
                                    PartDAL.BuscarPartidasPorRodada(_partida.Campeonato.ID, _partida.Rodada + 1);
                                if (proximasPartidas == null || proximasPartidas.Count == 0)
                                {
                                    proximaPartida = new Partida()
                                    {
                                        Campeonato = _partida.Campeonato,
                                        Data = _partida.Data,
                                        PlacarRegistrado = false,
                                        Rodada = _partida.Rodada + 1
                                    };
                                }
                                else
                                {
                                    proximaPartida = proximasPartidas[0];
                                }
                                break;
                            case TipoFinaisPontosCorridos.QUARTAS:
                                break;
                            case TipoFinaisPontosCorridos.OITAVAS:
                                break;
                            default:
                                throw new ArgumentOutOfRangeException();
                        }

                        //Busca a próxima partida
                        //var proximasPartidas = 
                        //    PartDAL.BuscarPartidasPorRodada(_partida.Campeonato.ID, _partida.Rodada + 1);

                        //if (proximasPartidas == null)
                        //{
                            
                        //}

                        //bool jogador1Ganhou = _partida.GolsJogador1.Value > _partida.GolsJogador2;
                        //bool empate = _partida.GolsJogador1.Value == _partida.GolsJogador2;

                        // = new Partida()
                        //                         {
                        //                             Campeonato = _partida.Campeonato,
                        //                             Data = _partida.Data,
                        //                             PlacarRegistrado = false,
                        //                             Rodada = _partida.Rodada + 1
                        //                         };

                        //if (jogador1Ganhou || empate)
                        //{
                        //    //O jogador 1 sempre é o de melhor campanha
                        //    proximaPartida.Jogador1 = _partida.Jogador1;
                        //} 
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.Error("Ocorreu um erro ao registrar o placar da partida.", ex);
                throw;
            }
        }

        public static long AtualizaPartidaAoVivo(long _partidaID, long _jogadorID, long _jogadorVirtualID, string _jogadorVirtualNome,
            int _golsMarcados, bool _cartaoAmarelo, bool _cartaoVermelho)
        {
            Partida partida = PartDAL.BuscarPartida(_partidaID);

            JogadorVirtual jog = _jogadorVirtualID <= 0 ? 
                JogVirtualDAL.CriaJogadorVirtual(_jogadorID, _jogadorVirtualNome) : JogVirtualDAL.BuscarJogadorVirtual(_jogadorVirtualID);

            IList<PartidaJogadorVirtual> partsJogVirt = JogVirtualDAL.BuscarEstatisticasJogVitualPartida(_partidaID);
            var partJogVirt = partsJogVirt.FirstOrDefault(_partJog => _partJog.JogadorVirtual.ID == jog.ID);

            if (partJogVirt == null)
            {
                partJogVirt = new PartidaJogadorVirtual {JogadorVirtual = jog, PartidaDisputada = new Partida()};
                partJogVirt.PartidaDisputada = partida;
                partJogVirt.CartoesAmarelos = _cartaoAmarelo ? 1 : 0;
                partJogVirt.QtdeGolsMarcados = _golsMarcados;
                partJogVirt.RecebeuCartaoVermelho = _cartaoVermelho;
                JogVirtualDAL.InserePartidaJogadorVirtual(partJogVirt);
            }
            else
            {
                partJogVirt.RecebeuCartaoVermelho = _cartaoVermelho;
                partJogVirt.QtdeGolsMarcados += _golsMarcados;
                partJogVirt.CartoesAmarelos += _cartaoAmarelo ? 1 : 0;
                JogVirtualDAL.AtualizaPartidaJogadorVirtual(partJogVirt);
            }

            bool ehJogador1 = partida.Jogador1.ID == _jogadorID;
            if (ehJogador1)
            {
                if (partida.GolsJogador1.HasValue)
                {
                    partida.GolsJogador1 += _golsMarcados;
                }
                else
                {
                    //o segundo jogador não pode ficar null para não estourar erros em outros lugares
                    partida.GolsJogador1 = _golsMarcados;
                    partida.GolsJogador2 = 0;
                }
            }
            else
            {
                if (partida.GolsJogador2.HasValue)
                {
                    partida.GolsJogador2 += _golsMarcados;
                }
                else
                {
                    partida.GolsJogador2 = _golsMarcados;
                    //o segundo jogador não pode ficar null para não estourar erros em outros lugares
                    partida.GolsJogador1 = 0;
                }
            }

            PartDAL.AtualizaPartida(partida);

            return jog.ID;
        }

        public static void RegistraTextoPartidaAoVivo(long _partidaID, long _jogadorID, long _jogadorVirtualID,
                                                      TipoAcaoPartida _acaoPartida)
        {
            Partida partida = PartDAL.BuscarPartida(_partidaID);
            bool ehJogador1 = partida.Jogador1.ID == _jogadorID;
            string textoAcao;

            switch (_acaoPartida)
            {
                case TipoAcaoPartida.MarcouGol:
                    JogadorVirtual jogVirtual = JogVirtualDAL.BuscarJogadorVirtual(_jogadorVirtualID);
                    textoAcao = GetRandomTextoAcao("GOL_");
                    textoAcao = string.Format(textoAcao,
                                              ehJogador1
                                                  ? partida.Jogador1.TimeEscolhido
                                                  : partida.Jogador2.TimeEscolhido,
                                              jogVirtual.Nome,
                                              ehJogador1 ? partida.Jogador1.Nome : partida.Jogador2.Nome);
                    break;
                case TipoAcaoPartida.RecebeuCartaoAmarelo:
                    JogadorVirtual jogVirtualAmarelo = JogVirtualDAL.BuscarJogadorVirtual(_jogadorVirtualID);
                    textoAcao = GetRandomTextoAcao("CARTAO_AMARELO_");
                    textoAcao = string.Format(textoAcao, jogVirtualAmarelo.Nome, ehJogador1
                                                                                     ? partida.Jogador1.TimeEscolhido
                                                                                     : partida.Jogador2.TimeEscolhido,
                                              ehJogador1 ? partida.Jogador1.Nome : partida.Jogador2.Nome);
                    break;
                case TipoAcaoPartida.RecebeuCartaoVermelho:
                    JogadorVirtual jogVirtualVermelho = JogVirtualDAL.BuscarJogadorVirtual(_jogadorVirtualID);
                    textoAcao = GetRandomTextoAcao("CARTAO_VERMELHO_");
                    textoAcao = string.Format(textoAcao, jogVirtualVermelho.Nome, ehJogador1
                                                                                      ? partida.Jogador1.TimeEscolhido
                                                                                      : partida.Jogador2.TimeEscolhido,
                                              ehJogador1 ? partida.Jogador1.Nome : partida.Jogador2.Nome);
                    break;
                case TipoAcaoPartida.InicioPartida:
                    textoAcao = GetRandomTextoAcao("INICIO_PARTIDA_");
                    textoAcao = string.Format(textoAcao, partida.Jogador1.Nome, partida.Jogador1.TimeEscolhido,
                                              partida.Jogador2.Nome, partida.Jogador2.TimeEscolhido);
                    break;
                case TipoAcaoPartida.FimPartida:
                    if (partida.GolsJogador1.Value == partida.GolsJogador2.Value)
                    {
                        textoAcao = partida.GolsJogador1.Value == 0
                                        ? ResTextosAcaoPartida.FIM_PARTIDA_ZERO_A_ZERO
                                        : GetRandomTextoAcao("FIM_PARTIDA_EMPATE_");
                    }
                    else
                    {
                        int golsJog1 = partida.GolsJogador1.Value;
                        int golsJog2 = partida.GolsJogador2.Value;
                        string nomeJogadorVitorioso = golsJog1 > golsJog2
                                                          ? partida.Jogador1.Nome
                                                          : partida.Jogador2.Nome;

                        textoAcao = GetRandomTextoAcao("FIM_PARTIDA_VITORIA_");
                        textoAcao = golsJog1 > golsJog2
                                        ? string.Format(textoAcao, nomeJogadorVitorioso, golsJog1, golsJog2)
                                        : string.Format(textoAcao, nomeJogadorVitorioso, golsJog2, golsJog1);
                    }
                    break;
                default:
                    throw new ArgumentOutOfRangeException("_acaoPartida");
            }

            PartDAL.RegistraAcaoPartida(partida.ID, _acaoPartida, textoAcao);
        }

        private static string GetRandomTextoAcao(string _chaveBase)
        {
            var random = new Random();
            int indiceTexto = random.Next(3);
            string textoAcao =
                ResTextosAcaoPartida.ResourceManager.GetString(string.Format(_chaveBase + "{0}", indiceTexto));
            return textoAcao;
        }

        public static void ConfirmaFinalPartida(long _partida)
        {
            Partida p = PartDAL.BuscarPartida(_partida);

            //corrige o problema de quando não aconteceu nada durante a partida
            if (!p.GolsJogador1.HasValue)
            {
                p.GolsJogador1 = 0;
            }

            if (!p.GolsJogador2.HasValue)
            {
                p.GolsJogador2 = 0;
            }
            
            RegistraPlacar(p.Campeonato, p);
            
            IList<PartidaJogadorVirtual> lst = JogVirtualDAL.BuscarEstatisticasJogVitualPartida(_partida);
            foreach (var partJog in lst)
            {
                AtualizaEstatisticasJogadorVirtual(partJog.JogadorVirtual.Jogador, partJog);
            }
        }

        public static void AtualizaEstatisticasJogadorVirtual(Jogador _jogador, PartidaJogadorVirtual _partidaJogadorVirtual)
        {
            JogadorVirtual jog =
                JogVirtualDAL.BuscarJogadorVirtual(_jogador.ID, _partidaJogadorVirtual.JogadorVirtual.Nome);

            _partidaJogadorVirtual.PartidaDisputada = PartDAL.BuscarPartida(_partidaJogadorVirtual.PartidaDisputada.ID);

            if (jog == null)
            {
                jog = JogVirtualDAL.CriaJogadorVirtual(_jogador.ID, _partidaJogadorVirtual.JogadorVirtual.Nome);
            }

            bool existeRegistroPartida = JogVirtualDAL.ExisteRegistroPartida(_partidaJogadorVirtual.PartidaDisputada.ID,
                                                _partidaJogadorVirtual.JogadorVirtual.Nome);

            jog.TotalGolsMarcados += _partidaJogadorVirtual.QtdeGolsMarcados;

            /** REGRAS DOS CARTÕES **
                 * § 2º - Na aplicação dos cartões amarelos deve prevalecer o seguinte
                    protocolo:
                    1. quando um atleta for advertido com o cartão amarelo e posteriormente
                    for expulso de campo pela exibição direta do cartão vermelho, aquele
                    cartão amarelo anteriormente exibido permanecerá em vigor para o
                    cômputo dos três cartões que resultarão em impedimento automático;
                    2. quando o cartão amarelo a que se refere o item anterior for o terceiro
                    da série, o atleta será penalizado com dois impedimentos automáticos,
                    sendo um pela seqüência dos três cartões amarelos, e outro pelo
                    recebimento do cartão vermelho;
                    3. quando, na mesma partida, um atleta recebe um primeiro cartão
                    amarelo e posteriormente recebe um segundo cartão amarelo, do
                    que resulta a exibição do cartão vermelho, os cartões amarelos que
                    precederam ao vermelho não serão considerados para o cômputo dos
                    três cartões amarelos que resultam em o impedimento automático.
                 * 
                 * */

            bool suspensoAntes = jog.Suspenso;

            if (_partidaJogadorVirtual.CartoesAmarelos == 2)
            {
                jog.TotalCartoesVermelhos++;
                jog.Suspenso = true;
            }
            else
            {
                if (_partidaJogadorVirtual.RecebeuCartaoVermelho)
                {
                    jog.TotalCartoesVermelhos++;
                    jog.Suspenso = true;
                }

                if (_partidaJogadorVirtual.CartoesAmarelos > 0)
                {
                    jog.TotalCartoesAmarelos++;
                    jog.Suspenso = jog.TotalCartoesAmarelos % 3 == 0;
                }
            }//if jogador recebeu 2 cartoes amarelos

            //Se já estava suspenso antes
            if (suspensoAntes)
            {
                if (jog.Suspenso)
                {
                    jog.QtdeRodadasSuspenso++;
                }
            }
            else
            {
                if (jog.Suspenso)
                {
                    jog.RodadaSuspensao = _partidaJogadorVirtual.PartidaDisputada.Rodada;
                    jog.QtdeRodadasSuspenso = 1;
                }
            }

            JogVirtualDAL.AtualizaEstatisticas(jog);

            if (!existeRegistroPartida)
            {
                _partidaJogadorVirtual.JogadorVirtual = jog;
                JogVirtualDAL.InserePartidaJogadorVirtual(_partidaJogadorVirtual);
            }
        }

        private static void AtualizaPontuacao(Pontuacao _pJ1, Pontuacao _pJ2, int _golsJ1, int _golsJ2)
        {
            _pJ1.GolsPro += _golsJ1;
            _pJ1.GolsContra += _golsJ2;
            _pJ2.GolsPro += _golsJ2;
            _pJ2.GolsContra += _golsJ1;

            if (_golsJ1 > _golsJ2)
            {
                _pJ1.NumVitorias++;
                _pJ2.NumDerrotas++;
            }
            else if (_golsJ1 == _golsJ2)
            {
                _pJ1.NumEmpates++;
                _pJ2.NumEmpates++;
            }
            else
            {
                _pJ2.NumVitorias++;
                _pJ1.NumDerrotas++;
            }

            _pJ1.TotalPontos = _pJ1.NumVitorias*3 + _pJ1.NumEmpates;

            _pJ2.TotalPontos = _pJ2.NumVitorias*3 + _pJ2.NumEmpates;

            PontDAL.AtualizaPontuacaoJogador(_pJ1);
            PontDAL.AtualizaPontuacaoJogador(_pJ2);
        }

        public static void ExcluirPartida(Campeonato _campeonato, Partida _partida)
        {
            //TODO: COMO FAZER PARA APAGAR UMA PARTIDA?
        }

        public static IList<Partida> BuscarPartidasDoDia(Campeonato _campeonato, DateTime _data)
        {
            try
            {
                if (_campeonato == null || _campeonato.ID <= 0)
                {
                    throw new ApplicationException("Campeonato inválido.");
                }

                return PartDAL.BuscarPartidasPorData(_campeonato.ID, _data);
            }
            catch (Exception ex)
            {
                Logger.Error("Ocorreu um erro ao buscar partidas por data.", ex);
                throw;
            }
        }

        public static bool ValidaPartida(int _campeonatoID, Partida _partida, out int _rodada)
        {
            try
            {
                return PartDAL.ValidaPartida(_campeonatoID, _partida, out _rodada);
            }
            catch (Exception ex)
            {
                Logger.Error("Ocorreu um erro ao validar a partida.", ex);
                throw;
            }
        }

        public static IList<Partida> BuscarConfrontosAnteriores(Campeonato _campeonato, Partida _partidaAtual)
        {
            if (_campeonato == null || _campeonato.ID <= 0)
            {
                throw new ApplicationException("Campeonato inválido.");
            }

            Campeonato camp = CampDAL.BuscarCampeonato(_campeonato.ID);

            if (_campeonato == null)
            {
                throw new ApplicationException("Campeonato inválido.");
            }

            if (!(camp.EstiloCampeonato == TipoCampeonato.PONTOS_CORRIDOS_DOIS_TURNOS ||
                camp.EstiloCampeonato == TipoCampeonato.PONTOS_CORRIDOS_DOIS_TURNOS_COM_FINAIS))
            {
                return null;
            }

            if (_partidaAtual == null || _partidaAtual.ID <= 0)
            {
                throw new ApplicationException("Partida inválida.");
            }

            Partida partidaAtual = PartDAL.BuscarPartida(_partidaAtual.ID);

            if (_partidaAtual == null)
            {
                throw new ApplicationException("Partida inválida.");
            }

            return PartDAL.BuscaConfrontosAnteriores(partidaAtual.Jogador1.ID, partidaAtual.Jogador2.ID, _partidaAtual.Rodada);
        }

        public static IList<Partida> BuscarJogosFinais(Campeonato _campeonato)
        {
            if (_campeonato == null || _campeonato.ID <= 0)
            {
                throw new ApplicationException("Campeonato inválido.");
            }

            Campeonato camp = CampDAL.BuscarCampeonato(_campeonato.ID);

            if (!(camp.EstiloCampeonato == TipoCampeonato.PONTOS_CORRIDOS_DOIS_TURNOS_COM_FINAIS ||
                camp.EstiloCampeonato == TipoCampeonato.PONTOS_CORRIDOS_UM_TURNO_COM_FINAIS))
            {
                return null;
            }

            bool acabouPontosCorridos = PartDAL.AcabouPontosCorridos(camp.ID);

            if (acabouPontosCorridos)
            {
                var qtdeJogadoresFinal = (int)camp.QtdeTimesClassificadosFinais;
                IList<Jogador> jogadores = CampDAL.BuscarRankingCampeonato(camp.ID);
                var partidasFinais = new List<Partida>();
                for (int i = 0; i < qtdeJogadoresFinal / 2; i++)
                {
                    var p = new Partida
                                {
                                    Data = DateTime.Now.AddDays(7),
                                    Jogador1 = jogadores[i],
                                    Jogador2 = jogadores[qtdeJogadoresFinal - i - 1],
                                    Rodada = camp.QuantidadeRodadas + 1
                                };
                    partidasFinais.Add(p);
                }

                return partidasFinais;
            }

            return null;
        }

        private static void LimpaSuspensoes(Jogador _jogador)
        {
            List<JogadorVirtual> jogadoresSuspensos =
                JogVirtualDAL.BuscarJogadoresSuspensos(_jogador.ID);

            foreach (JogadorVirtual jogadorSuspenso in jogadoresSuspensos)
            {
                jogadorSuspenso.QtdeRodadasSuspenso--;
                if (jogadorSuspenso.QtdeRodadasSuspenso <= 0)
                {
                    jogadorSuspenso.Suspenso = false;
                    jogadorSuspenso.QtdeRodadasSuspenso = 0;
                    jogadorSuspenso.RodadaSuspensao = 0;
                }

                JogVirtualDAL.AtualizaEstatisticas(jogadorSuspenso);
            }
        }

        public static void CadastraPartidasAleatorias(Campeonato _camp)
        {
            IList<Jogador> jogadores = JogDAL.BuscarJogadores(_camp.ID);
            int maxJogadores = jogadores.Count;

            Campeonato camp = CampDAL.BuscarCampeonato(_camp.ID);

            var jogadores1 = new List<Jogador>(maxJogadores / 2);
            var jogadores2 = new List<Jogador>(maxJogadores / 2);

            DistribuiJogadoresRandom(jogadores1, jogadores2, jogadores);

            //If n is even, you have n - 1 rounds, if n is odd you have n rounds
            IEnumerable<Partida> partidas = 
                SorteioRoundRobinTournament(jogadores1, jogadores2, (maxJogadores % 2 == 0 ? maxJogadores - 1 : maxJogadores));

            foreach (Partida partida in partidas)
            {
                if (partida.Jogador1.ID > 0 && partida.Jogador2.ID > 0)
                {
                    int rodada;
                    if (ValidaPartida(camp.ID, partida, out rodada))
                    {
                        CadastrarPartida(camp, partida);
                    }
                }
            }
        }

        private static IEnumerable<Partida> SorteioRoundRobinTournament(List<Jogador> _conjunto1, List<Jogador> _conjunto2, int _numeroRodadas)
        {
            var partidas = new List<Partida>();

            //rodada 1
            //The standard algorithm for round-robins is to assign each competitor a number, and pair them off in the first round
            int contador = 0;
            foreach (Jogador jog in _conjunto1)
            {
                var p = new Partida
                            {
                                Rodada = 1,
                                Data = DateTime.Now,
                                Jogador1 = jog,
                                Jogador2 = _conjunto2[contador++],
                                PlacarRegistrado = false
                            };

                partidas.Add(p);
            }

            //then fix one competitor (number one in this example) and rotate the others clockwise one position
            for (int i = 1; i < _numeroRodadas; i++)
            {
                //rodada i
                Jogador ultimoJog1 = _conjunto1[_conjunto1.Count - 1];
                Jogador primeiroJog2 = _conjunto2[0];
                _conjunto1.Remove(ultimoJog1);
                _conjunto2.Remove(primeiroJog2);
                _conjunto1.Insert(1, primeiroJog2);
                _conjunto2.Add(ultimoJog1);
                contador = 0;
                foreach (Jogador jog in _conjunto1)
                {
                    var p = new Partida
                                {
                                    Rodada = i + 1,
                                    Data = DateTime.Now,
                                    Jogador1 = jog,
                                    Jogador2 = _conjunto2[contador++],
                                    PlacarRegistrado = false
                                };

                    partidas.Add(p);
                }
            }

            return partidas;
        }

        private static void DistribuiJogadoresRandom(List<Jogador> _conjunto1, List<Jogador> _conjunto2, IList<Jogador> _jogadores)
        {
            if (_jogadores.Count % 2 != 0)
            {
                var j = new Jogador {Nome = "BYE", ID = -1};

                _jogadores.Add(j);
            }

            var jogTemp = new List<Jogador>(_jogadores);
            for (int i = 0; i < _jogadores.Count/2; i++)
            {
                var r = new Random();
                int index = r.Next(jogTemp.Count);
                _conjunto1.Add(jogTemp[index]);
                jogTemp.RemoveAt(index);
                r = new Random();
                index = r.Next(jogTemp.Count);
                _conjunto2.Add(jogTemp[index]);
                jogTemp.RemoveAt(index);
            }
        }

        public static List<PartidaJogadorVirtual> BuscarEstatisticasJogVitualPartida(long _partidaID)
        {
            return JogVirtualDAL.BuscarEstatisticasJogVitualPartida(_partidaID);
        }

        public static IList<Partida> BuscarTodasPartidasDoDia(DateTime _data)
        {
            IList<Campeonato> campeonatosAtivos =
                CampDAL.BuscarTodosCampeonatos(true, false);

            var partidasDoDia = new List<Partida>();

            foreach (Campeonato campeonato in campeonatosAtivos)
            {
                IList<Partida> partidas = PartDAL.BuscarPartidasPorData(campeonato.ID, _data);
                partidasDoDia.AddRange(partidas);
            }

            return partidasDoDia;
        }

        public static IList<Partida> BuscarTodasPartidas(Campeonato _camp)
        {
            if (_camp == null || _camp.ID <= 0)
            {
                throw new ApplicationException("Campeonato inválido.");
            }
            return PartDAL.BuscarTodasPartidas(_camp.ID);
        }

        public static Partida BuscarPartida(long _partidaID)
        {
            return PartDAL.BuscarPartida(_partidaID);
        }

        public static IEnumerable<AcaoPartida> BuscarTodasAcoesPartida(long _partidaID)
        {
            return PartDAL.BuscaAcoesPartida(_partidaID);
        }
    }
}
